In my previous post I explored bitcoin data from different exchagnes, we also covered some arbitrage-related data. In part 2 of this series I will explore alt coin realted data.
Data
The best source I know off to get alt-coin data is through PoloniexR. I have written an R function to help download data.
get_alt_data <- function(tz = "UTC"
, coin = c("ETH", "LTC")
, add_bitcoin = TRUE
, return_in_USDT = TRUE
, from = "2017-01-01"
, to = "2018-04-09"
, period = "D"
, verbose = FALSE){
# We will be using the public API
poloniex.public <- PoloniexPublicAPI()
# set the time zone to utc
Sys.setenv(tz = tz)
# convert from and to into time obj
from <- as.POSIXct(paste(from, tz, sep = ""))
to <- as.POSIXct(paste(to, tz, sep = ""))
# lists to store data.tables and xts objects
chart_list <- list()
dt_list <- list()
# make sure the coin pair is in upper case
coin <- toupper(coin)
coin_pairs <- paste0("BTC_", coin[coin != "BTC"])
if(add_bitcoin | return_in_USDT) coin_pairs <- c("USDT_BTC", coin_pairs)
# loop over the coins to get the data
for(i in coin_pairs){
if(verbose)
invisible(cat('\tGetting data for ', i, ' pair\n'))
# this is a list that will contain the chart data for each coin pair
try(chart_list[[i]] <- ReturnChartData(theObject = poloniex.public
, pair = i
, from = from
, to = to
, period = period)
, silent = TRUE)
# list to contain data.tables
try(dt_list[[i]] <- as.data.table(chart_list[[i]]), silent = TRUE)
}
# convert to data.table and make sure to add a column containing the pairs
coin_dt <- rbindlist(l = dt_list, use.names = TRUE, idcol = "pair")
# return data in usdt prices
if(return_in_USDT){
# to get the price of the alt coin in usdt is not that simple but we'll do it
# get a DT of the btc_usdt pair
btc_usd <- coin_dt[pair == "USDT_BTC"]
btc_usd <- btc_usd[, .(index, pair, weightedaverage)]
setnames(btc_usd, c("Date", "USDT_BTC_pair", "USDT_BTC_price"))
# get DT with only alt coins
alt_coins <- copy(coin_dt)#[pair != "USDT_BTC"]
# now we need to add an index to the alt_coins table, but first we have to rename the index column
alt_coins[, Date := index]
alt_coins[, index := 1:.N]
setkey(alt_coins, index)
# now merge the data tables
coin_dt_usdt <- merge(x = alt_coins, y = btc_usd, by = "Date")
# now calcualte the price in usdt
coin_dt_usdt[, price_usdt := ifelse(pair == "USDT_BTC", USDT_BTC_price, weightedaverage * USDT_BTC_price)]
# now get rid of the extra columns
coin_dt_usdt[, c("USDT_BTC_price", "USDT_BTC_pair") := NULL]
# we need to change some column names
col_names_to_change <- c("pair", "high", "low", "open", "close", "volume", "quotevolume", "weightedaverage")
col_names <- names(coin_dt_usdt)
col_names[col_names %in% col_names_to_change] <- paste0(col_names_to_change, '_btc')
setnames(coin_dt_usdt, col_names)
# add a column for the usdt pair
coin_dt_usdt[, pair_usdt := gsub("BTC_", "USDT_", pair_btc)]
# adjust col order
setcolorder(coin_dt_usdt, c(1:10, 12, 11))
# set key again
setkey(coin_dt_usdt, index)
# now get rid of the index column since it is not needed anymore
coin_dt_usdt[, index := NULL]
# now put together the return list
return_list <- list(alt_chart_list = chart_list, alt_dt = coin_dt, alt_usdt_dt = coin_dt_usdt)
}else{
return_list <- list(alt_chart_list = chart_list, alt_dt = coin_dt)
}
return(return_list)
}
poloniex_function can be used to download data for multiple coin at the same time. The function returns a data.table object with data for all coins in the function call. Even if the user doesn’t add bitcoin to the list of coins, the function adds bitcoin by default. This can be deactivated with the add_bitcoin argument. Here is an example
# get alt data for some coins
alt_data <- get_alt_data(return_in_USDT = T
, from = "2015-01-01"
, coin = c('ETH','XRP', 'BCH', 'LTC', 'NEO', 'XMR', 'DASH', 'XEM'))[['alt_usdt_dt']]
Error in rbindlist(l = dt_list, use.names = TRUE, idcol = "pair") :
attempt to set index 8211/8211 in SET_STRING_ELT
Let’s look at the data we just downloaded
head(alt_data)
The table shows the date, OHLC, Volume, and weightedaverage price in BTC. It also shows the pair and we added the price in USD.
Bitcoin-Altcoins Correlations
Wheneven I look at the prices of the coins available on my coinbase app I always get struck by the similarity of the price trends between the four coins available on coinbase: BTC, ETH, BCH, and LTC, see Figure below. So I thought it will be a good idea to explore the correlation in price trends between altcoins and bitcoin.
Let’s look at price trends of the coins we just downloaded. To better see potential correlations I am going to only zoon in on 2018.
p <- ggplot(alt_data[year(Date) == 2018], aes(x = Date, y = price_usdt, col = pair_usdt)) + geom_line()
p <- p + facet_wrap(~pair_usdt, scales = "free", ncol = 3) + theme_minimal() + theme(legend.position="none") + ylab("Price (USD)")
p

The figure above shows that some coins seems to be more correlated with Bitcoin than others. The figure also shows that this variablity between Bitcoin and another coin varies over time. More on this below.
Tyring to find correlations bewteen time series data using Pearson correlation coefficient or other metrics used with stationary data, time series is not a form of stationary data, can give misleading results. Similar trends in time series data can also be very misleading, a nice article on this topic can be found here. And always remember that Correlation doesn’t guarantee Causation
Bottom line is the following, one has to be careful when cross-correlating time serice. In order to perform proper correlation analysis we need to add some new variables to our table.
Percentage Daily Change
Percentage daily change calculates the price change of a coin over a period of a day. Let’s add that to the table. Notice that we are calcualting this variable using the USD price, and not the price in Bitcoin.
# add daily price change
alt_data[, pct_change := Delt(price_usdt), by = pair_usdt]
Normalized Price in USD
Since the prices vary a lot, both overtime for the same coin and between coins, we will add a variable of the normalized price in USD.
# add normalized prices in udst
alt_data[, price_usdt_norm := price_usdt/max(price_usdt), by = pair_usdt]
Let’s look at the percentage daily changes of the altcoins between 2015 and today.
# plot the percent changes
p <- ggplot(alt_data[Date > ymd("2015-01-01")], aes(x = Date, y = (100*pct_change), col = pair_usdt)) + geom_line()
p <- p + ggtitle("% Daily Returns over time") + ylab("Daily Return (%)")
p <- p + annotate("text", x = as.POSIXct("2015-10-05"), y = 75, label = "@aousabdo",fontface="bold")
p <- p + theme_bw() + guides(col=guide_legend(title="Coin Pair"))
ggplotly(p)
Although the above figure is very cluttered, one thing is certain, percentage daily returns vary greatly for crypto. Let’s try to make this figure a bit easier to read
p <- ggplot(alt_data[Date > ymd("2015-01-01")], aes(x = Date, y = (100*pct_change), col = pair_usdt)) + geom_line() + facet_wrap(~ pair_usdt)
p <- p + ggtitle("Percentage Daily Returns over time") + ylab("Daily Return (%)")
p <- p + theme_bw() + theme(legend.position="none")
p

It is kind of surprising that Bitcoin has the least variability in daily returns. The nice big spike around April 2nd 2017 shows a percentage daily return of ~88% for XRP, this is the highest daily return I have seen!
Let’s look at the percentage daily returns for Bitcoin and Litecoin since they seem to be highly correlated. I am going to zoom in on the time period 2016-02-01 and 2016-05-01.
start_date <- ymd("2016-02-01")
end_date <- ymd("2016-05-01")
p <- ggplot(alt_data[pair_usdt %like% "BTC|LTC" & Date > start_date & Date < end_date], aes(x = Date, y = (100*pct_change), col = pair_usdt)) + geom_line() + theme_bw() + ylab("Price (USD)")
p

There clearly is a correlation between daily returns of BTC and LTC.
Now we’ll subset the data to only keep variables we are interested in
# subset data
alt_data_sub <- alt_data[, .(Date, pair_usdt, pct_change)]
We’ll do some data processing
# convert to wide format
alt_data_sub <- spread(data = alt_data_sub, key = "pair_usdt", value = "pct_change")
LS0tCnRpdGxlOiAiQSBRdWljayBMb29rIGF0IENyeXB0byBDdXJyZW5jaWVzIHdpdGggUiAtIFBhcnQgMiIKb3V0cHV0OiBodG1sX25vdGVib29rCmF1dGhvcjogIkRyLiBBb3VzIFwiQWxleFwiIEFiZG8gQGFvdXNhYmRvIgplZGl0b3Jfb3B0aW9uczogCiAgY2h1bmtfb3V0cHV0X3R5cGU6IGlubGluZQotLS0KCkluIG15IHByZXZpb3VzIHBvc3QgSSBleHBsb3JlZCBiaXRjb2luIGRhdGEgZnJvbSBkaWZmZXJlbnQgZXhjaGFnbmVzLCB3ZSBhbHNvIGNvdmVyZWQgc29tZSBhcmJpdHJhZ2UtcmVsYXRlZCBkYXRhLiAKSW4gcGFydCAyIG9mIHRoaXMgc2VyaWVzIEkgd2lsbCBleHBsb3JlIGFsdCBjb2luIHJlYWx0ZWQgZGF0YS4gCgojIyBEYXRhClRoZSBiZXN0IHNvdXJjZSBJIGtub3cgb2ZmIHRvIGdldCBhbHQtY29pbiBkYXRhIGlzIHRocm91Z2ggW1BvbG9uaWV4Ul0oaHR0cHM6Ly9jcmFuLnItcHJvamVjdC5vcmcvd2ViL3BhY2thZ2VzL1BvbG9uaWV4Ui9pbmRleC5odG1sKS4gSSBoYXZlIHdyaXR0ZW4gYW4gUiBmdW5jdGlvbiB0byBoZWxwIGRvd25sb2FkIGRhdGEuIAoKYGBge3IgcG9sb25pZXhfZnVuY3Rpb259CmdldF9hbHRfZGF0YSA8LSBmdW5jdGlvbih0eiA9ICJVVEMiCiAgICAgICAgICAgICAgICAgICAgICAgICAsIGNvaW4gPSBjKCJFVEgiLCAiTFRDIikKICAgICAgICAgICAgICAgICAgICAgICAgICwgYWRkX2JpdGNvaW4gPSBUUlVFCiAgICAgICAgICAgICAgICAgICAgICAgICAsIHJldHVybl9pbl9VU0RUID0gVFJVRQogICAgICAgICAgICAgICAgICAgICAgICAgLCBmcm9tID0gIjIwMTctMDEtMDEiCiAgICAgICAgICAgICAgICAgICAgICAgICAsIHRvID0gIjIwMTgtMDQtMDkiCiAgICAgICAgICAgICAgICAgICAgICAgICAsIHBlcmlvZCA9ICJEIgogICAgICAgICAgICAgICAgICAgICAgICAgLCB2ZXJib3NlID0gRkFMU0UpewogIAogICMgV2Ugd2lsbCBiZSB1c2luZyB0aGUgcHVibGljIEFQSQogIHBvbG9uaWV4LnB1YmxpYyA8LSBQb2xvbmlleFB1YmxpY0FQSSgpCiAgCiAgIyBzZXQgdGhlIHRpbWUgem9uZSB0byB1dGMKICBTeXMuc2V0ZW52KHR6ID0gdHopCiAgCiAgIyBjb252ZXJ0IGZyb20gYW5kIHRvIGludG8gdGltZSBvYmoKICBmcm9tICA8LSBhcy5QT1NJWGN0KHBhc3RlKGZyb20sIHR6LCBzZXAgPSAiIikpCiAgdG8gICAgPC0gYXMuUE9TSVhjdChwYXN0ZSh0bywgdHosIHNlcCA9ICIiKSkKICAKICAjIGxpc3RzIHRvIHN0b3JlIGRhdGEudGFibGVzIGFuZCB4dHMgb2JqZWN0cwogIGNoYXJ0X2xpc3QgPC0gbGlzdCgpCiAgZHRfbGlzdCAgICA8LSBsaXN0KCkKICAKICAjIG1ha2Ugc3VyZSB0aGUgY29pbiBwYWlyIGlzIGluIHVwcGVyIGNhc2UKICBjb2luICAgICAgIDwtIHRvdXBwZXIoY29pbikKICBjb2luX3BhaXJzIDwtIHBhc3RlMCgiQlRDXyIsIGNvaW5bY29pbiAhPSAiQlRDIl0pCiAgaWYoYWRkX2JpdGNvaW4gfCByZXR1cm5faW5fVVNEVCkgY29pbl9wYWlycyA8LSBjKCJVU0RUX0JUQyIsIGNvaW5fcGFpcnMpCiAgCiAgIyBsb29wIG92ZXIgdGhlIGNvaW5zIHRvIGdldCB0aGUgZGF0YQogIGZvcihpIGluIGNvaW5fcGFpcnMpewogICAgaWYodmVyYm9zZSkKICAgICAgaW52aXNpYmxlKGNhdCgnXHRHZXR0aW5nIGRhdGEgZm9yICcsIGksICcgcGFpclxuJykpCiAgICAKICAgICMgdGhpcyBpcyBhIGxpc3QgdGhhdCB3aWxsIGNvbnRhaW4gdGhlIGNoYXJ0IGRhdGEgZm9yIGVhY2ggY29pbiBwYWlyCiAgICB0cnkoY2hhcnRfbGlzdFtbaV1dIDwtIFJldHVybkNoYXJ0RGF0YSh0aGVPYmplY3QgPSBwb2xvbmlleC5wdWJsaWMKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgLCBwYWlyICAgICAgPSBpCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICwgZnJvbSAgICAgID0gZnJvbQogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAsIHRvICAgICAgICA9IHRvCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICwgcGVyaW9kICAgID0gcGVyaW9kKQogICAgICAgICwgc2lsZW50ID0gVFJVRSkKICAgIAogICAgIyBsaXN0IHRvIGNvbnRhaW4gZGF0YS50YWJsZXMgCiAgICB0cnkoZHRfbGlzdFtbaV1dIDwtIGFzLmRhdGEudGFibGUoY2hhcnRfbGlzdFtbaV1dKSwgc2lsZW50ID0gVFJVRSkKICB9CiAgCiAgIyBjb252ZXJ0IHRvIGRhdGEudGFibGUgYW5kIG1ha2Ugc3VyZSB0byBhZGQgYSBjb2x1bW4gY29udGFpbmluZyB0aGUgcGFpcnMKICBjb2luX2R0IDwtIHJiaW5kbGlzdChsID0gZHRfbGlzdCwgdXNlLm5hbWVzID0gVFJVRSwgaWRjb2wgPSAicGFpciIpCiAgCiAgIyByZXR1cm4gZGF0YSBpbiB1c2R0IHByaWNlcwogIGlmKHJldHVybl9pbl9VU0RUKXsKICAgICMgdG8gZ2V0IHRoZSBwcmljZSBvZiB0aGUgYWx0IGNvaW4gaW4gdXNkdCBpcyBub3QgdGhhdCBzaW1wbGUgYnV0IHdlJ2xsIGRvIGl0CiAgICAjIGdldCBhIERUIG9mIHRoZSBidGNfdXNkdCBwYWlyCiAgICBidGNfdXNkIDwtIGNvaW5fZHRbcGFpciA9PSAiVVNEVF9CVEMiXQogICAgYnRjX3VzZCA8LSBidGNfdXNkWywgLihpbmRleCwgcGFpciwgd2VpZ2h0ZWRhdmVyYWdlKV0KICAgIHNldG5hbWVzKGJ0Y191c2QsIGMoIkRhdGUiLCAiVVNEVF9CVENfcGFpciIsICJVU0RUX0JUQ19wcmljZSIpKQogICAgCiAgICAjIGdldCBEVCB3aXRoIG9ubHkgYWx0IGNvaW5zCiAgICBhbHRfY29pbnMgPC0gY29weShjb2luX2R0KSNbcGFpciAhPSAiVVNEVF9CVEMiXQogICAgCiAgICAjIG5vdyB3ZSBuZWVkIHRvIGFkZCBhbiBpbmRleCB0byB0aGUgYWx0X2NvaW5zIHRhYmxlLCBidXQgZmlyc3Qgd2UgaGF2ZSB0byByZW5hbWUgdGhlIGluZGV4IGNvbHVtbgogICAgYWx0X2NvaW5zWywgRGF0ZSA6PSBpbmRleF0KICAgIGFsdF9jb2luc1ssIGluZGV4IDo9IDE6Lk5dCiAgICBzZXRrZXkoYWx0X2NvaW5zLCBpbmRleCkKICAgIAogICAgIyBub3cgbWVyZ2UgdGhlIGRhdGEgdGFibGVzCiAgICBjb2luX2R0X3VzZHQgPC0gbWVyZ2UoeCA9IGFsdF9jb2lucywgeSA9IGJ0Y191c2QsIGJ5ID0gIkRhdGUiKQogICAgCiAgICAjIG5vdyBjYWxjdWFsdGUgdGhlIHByaWNlIGluIHVzZHQKICAgIGNvaW5fZHRfdXNkdFssIHByaWNlX3VzZHQgOj0gaWZlbHNlKHBhaXIgPT0gIlVTRFRfQlRDIiwgVVNEVF9CVENfcHJpY2UsIHdlaWdodGVkYXZlcmFnZSAqIFVTRFRfQlRDX3ByaWNlKV0KICAgIAogICAgIyBub3cgZ2V0IHJpZCBvZiB0aGUgZXh0cmEgY29sdW1ucwogICAgY29pbl9kdF91c2R0WywgYygiVVNEVF9CVENfcHJpY2UiLCAiVVNEVF9CVENfcGFpciIpIDo9IE5VTExdCiAgICAKICAgICMgd2UgbmVlZCB0byBjaGFuZ2Ugc29tZSBjb2x1bW4gbmFtZXMKICAgIGNvbF9uYW1lc190b19jaGFuZ2UgPC0gYygicGFpciIsICJoaWdoIiwgImxvdyIsICJvcGVuIiwgImNsb3NlIiwgInZvbHVtZSIsICJxdW90ZXZvbHVtZSIsICJ3ZWlnaHRlZGF2ZXJhZ2UiKQogICAgY29sX25hbWVzIDwtIG5hbWVzKGNvaW5fZHRfdXNkdCkKICAgIGNvbF9uYW1lc1tjb2xfbmFtZXMgJWluJSBjb2xfbmFtZXNfdG9fY2hhbmdlXSA8LSBwYXN0ZTAoY29sX25hbWVzX3RvX2NoYW5nZSwgJ19idGMnKQogICAgCiAgICBzZXRuYW1lcyhjb2luX2R0X3VzZHQsIGNvbF9uYW1lcykKICAgIAogICAgIyBhZGQgYSBjb2x1bW4gZm9yIHRoZSB1c2R0IHBhaXIKICAgIGNvaW5fZHRfdXNkdFssIHBhaXJfdXNkdCA6PSBnc3ViKCJCVENfIiwgIlVTRFRfIiwgcGFpcl9idGMpXQogICAgCiAgICAjIGFkanVzdCBjb2wgb3JkZXIKICAgIHNldGNvbG9yZGVyKGNvaW5fZHRfdXNkdCwgYygxOjEwLCAxMiwgMTEpKQogICAgCiAgICAjIHNldCBrZXkgYWdhaW4KICAgIHNldGtleShjb2luX2R0X3VzZHQsIGluZGV4KQogICAgCiAgICAjIG5vdyBnZXQgcmlkIG9mIHRoZSBpbmRleCBjb2x1bW4gc2luY2UgaXQgaXMgbm90IG5lZWRlZCBhbnltb3JlCiAgICBjb2luX2R0X3VzZHRbLCBpbmRleCA6PSBOVUxMXQogICAgCiAgICAjIG5vdyBwdXQgdG9nZXRoZXIgdGhlIHJldHVybiBsaXN0ICAKICAgIHJldHVybl9saXN0IDwtIGxpc3QoYWx0X2NoYXJ0X2xpc3QgPSBjaGFydF9saXN0LCBhbHRfZHQgPSBjb2luX2R0LCBhbHRfdXNkdF9kdCA9IGNvaW5fZHRfdXNkdCkKICB9ZWxzZXsKICAgIHJldHVybl9saXN0IDwtIGxpc3QoYWx0X2NoYXJ0X2xpc3QgPSBjaGFydF9saXN0LCBhbHRfZHQgPSBjb2luX2R0KQogIH0KICAKICByZXR1cm4ocmV0dXJuX2xpc3QpCn0KYGBgCgogYHIgb3B0c19jdXJyZW50JGdldCgibGFiZWwiKWAgY2FuIGJlIHVzZWQgdG8gZG93bmxvYWQgZGF0YSBmb3IgbXVsdGlwbGUgY29pbiBhdCB0aGUgc2FtZSB0aW1lLiBUaGUgZnVuY3Rpb24gcmV0dXJucyBhIGRhdGEudGFibGUgb2JqZWN0IHdpdGggZGF0YSBmb3IgYWxsIGNvaW5zIGluIHRoZSBmdW5jdGlvbiBjYWxsLiBFdmVuIGlmIHRoZSB1c2VyIGRvZXNuJ3QgYWRkIGJpdGNvaW4gdG8gdGhlIGxpc3Qgb2YgY29pbnMsIHRoZSBmdW5jdGlvbiBhZGRzIGJpdGNvaW4gYnkgZGVmYXVsdC4gVGhpcyBjYW4gYmUgZGVhY3RpdmF0ZWQgd2l0aCB0aGUgYWRkX2JpdGNvaW4gYXJndW1lbnQuIEhlcmUgaXMgYW4gZXhhbXBsZSAKYGBge3J9CiMgZ2V0IGFsdCBkYXRhIGZvciBzb21lIGNvaW5zCmFsdF9kYXRhIDwtIGdldF9hbHRfZGF0YShyZXR1cm5faW5fVVNEVCA9IFQKICAgICAgICAgICAgICAgICAgICAgICAgICwgZnJvbSA9ICIyMDE1LTAxLTAxIgogICAgICAgICAgICAgICAgICAgICAgICAgLCBjb2luID0gYygnRVRIJywnWFJQJywgJ0JDSCcsICdMVEMnLCAnTkVPJywgJ1hNUicsICdEQVNIJywgJ1hFTScpKVtbJ2FsdF91c2R0X2R0J11dCmBgYAoKTGV0J3MgbG9vayBhdCB0aGUgZGF0YSB3ZSBqdXN0IGRvd25sb2FkZWQKYGBge3J9CmhlYWQoYWx0X2RhdGEpCmBgYApUaGUgdGFibGUgc2hvd3MgdGhlIGRhdGUsIE9ITEMsIFZvbHVtZSwgYW5kIHdlaWdodGVkYXZlcmFnZSBwcmljZSBpbiBCVEMuIEl0IGFsc28gc2hvd3MgdGhlIHBhaXIgYW5kIHdlIGFkZGVkIHRoZSBwcmljZSBpbiBVU0QuCgojIyBCaXRjb2luLUFsdGNvaW5zIENvcnJlbGF0aW9ucwpXaGVuZXZlbiBJIGxvb2sgYXQgdGhlIHByaWNlcyBvZiB0aGUgY29pbnMgYXZhaWxhYmxlIG9uIG15IFtjb2luYmFzZV0oaHR0cHM6Ly93d3cuY29pbmJhc2UuY29tLykgYXBwIEkgYWx3YXlzIGdldCBzdHJ1Y2sgYnkgdGhlIHNpbWlsYXJpdHkgb2YgdGhlIHByaWNlIHRyZW5kcyBiZXR3ZWVuIHRoZSBmb3VyIGNvaW5zIGF2YWlsYWJsZSBvbiBjb2luYmFzZTogQlRDLCBFVEgsIEJDSCwgYW5kIExUQywgc2VlIEZpZ3VyZSBiZWxvdy4gU28gSSB0aG91Z2h0IGl0IHdpbGwgYmUgYSBnb29kIGlkZWEgdG8gZXhwbG9yZSB0aGUgY29ycmVsYXRpb24gaW4gcHJpY2UgdHJlbmRzIGJldHdlZW4gYWx0Y29pbnMgYW5kIGJpdGNvaW4uIAoKIVtBcHBhcmVudCBjb3JyZWxhdGlvbiBiZXR3ZWVuIHRoZSBwcmljZXMgb2YgQml0Y29pbiBhbmQgb3RoZXIgY29pbnMgb24gY29pbmJhc2UuXSguLi9maWd1cmVzL2NvaW5iYXNlX3NjcmVlbnNob3QuanBnKXt3aWR0aD01MDBweH0KCkxldCdzIGxvb2sgYXQgcHJpY2UgdHJlbmRzIG9mIHRoZSBjb2lucyB3ZSBqdXN0IGRvd25sb2FkZWQuIFRvIGJldHRlciBzZWUgcG90ZW50aWFsIGNvcnJlbGF0aW9ucyBJIGFtIGdvaW5nIHRvIG9ubHkgem9vbiBpbiBvbiAyMDE4LiAKCmBgYHtyIGNvaW5fcHJpY2VzXzIwMTgsIGZpZy53aWR0aD0xMCwgZmlnLmhlaWdodD02LCBmaWcuY2FwPSJQcmljZXMgb2YgQml0Y29pbiBhbmQgb3RoZXIgYWx0Y29pbnMgaW4gMjAxOCJ9CnAgPC0gZ2dwbG90KGFsdF9kYXRhW3llYXIoRGF0ZSkgPT0gMjAxOF0sIGFlcyh4ID0gRGF0ZSwgeSA9ICBwcmljZV91c2R0LCBjb2wgPSBwYWlyX3VzZHQpKSArIGdlb21fbGluZSgpCnAgPC0gcCArIGZhY2V0X3dyYXAofnBhaXJfdXNkdCwgc2NhbGVzID0gImZyZWUiLCBuY29sID0gMykgKyB0aGVtZV9taW5pbWFsKCkgKyB0aGVtZShsZWdlbmQucG9zaXRpb249Im5vbmUiKSArIHlsYWIoIlByaWNlIChVU0QpIikKcApgYGAKClRoZSBmaWd1cmUgYWJvdmUgc2hvd3MgdGhhdCBzb21lIGNvaW5zIHNlZW1zIHRvIGJlIG1vcmUgY29ycmVsYXRlZCB3aXRoIEJpdGNvaW4gdGhhbiBvdGhlcnMuIFRoZSBmaWd1cmUgYWxzbyBzaG93cyB0aGF0IHRoaXMgdmFyaWFibGl0eSBiZXR3ZWVuIEJpdGNvaW4gYW5kIGFub3RoZXIgY29pbiB2YXJpZXMgb3ZlciB0aW1lLiBNb3JlIG9uIHRoaXMgYmVsb3cuCgpUeXJpbmcgdG8gZmluZCBjb3JyZWxhdGlvbnMgYmV3dGVlbiB0aW1lIHNlcmllcyBkYXRhIHVzaW5nIFBlYXJzb24gY29ycmVsYXRpb24gY29lZmZpY2llbnQgb3Igb3RoZXIgbWV0cmljcyB1c2VkIHdpdGggc3RhdGlvbmFyeSBkYXRhLCB0aW1lIHNlcmllcyBpcyBub3QgYSBmb3JtIG9mIHN0YXRpb25hcnkgZGF0YSwgY2FuIGdpdmUgbWlzbGVhZGluZyByZXN1bHRzLiBTaW1pbGFyIHRyZW5kcyBpbiB0aW1lIHNlcmllcyBkYXRhIGNhbiBhbHNvIGJlIHZlcnkgbWlzbGVhZGluZywgYSBuaWNlIGFydGljbGUgb24gdGhpcyB0b3BpYyBjYW4gYmUgZm91bmQgW2hlcmVdKGh0dHBzOi8vc3Zkcy5jb20vYXZvaWRpbmctY29tbW9uLW1pc3Rha2VzLXdpdGgtdGltZS1zZXJpZXMvKS4gQW5kIGFsd2F5cyByZW1lbWJlciB0aGF0ICoqQ29ycmVsYXRpb24gZG9lc24ndCBndWFyYW50ZWUgQ2F1c2F0aW9uKioKCkJvdHRvbSBsaW5lIGlzIHRoZSBmb2xsb3dpbmcsIG9uZSBoYXMgdG8gYmUgY2FyZWZ1bCB3aGVuIGNyb3NzLWNvcnJlbGF0aW5nIHRpbWUgc2VyaWNlLiBJbiBvcmRlciB0byBwZXJmb3JtIHByb3BlciBjb3JyZWxhdGlvbiBhbmFseXNpcyB3ZSBuZWVkIHRvIGFkZCBzb21lIG5ldyB2YXJpYWJsZXMgdG8gb3VyIHRhYmxlLgoKIyMjIFBlcmNlbnRhZ2UgRGFpbHkgQ2hhbmdlClBlcmNlbnRhZ2UgZGFpbHkgY2hhbmdlIGNhbGN1bGF0ZXMgdGhlIHByaWNlIGNoYW5nZSBvZiBhIGNvaW4gb3ZlciBhIHBlcmlvZCBvZiBhIGRheS4gTGV0J3MgYWRkIHRoYXQgdG8gdGhlIHRhYmxlLiBOb3RpY2UgdGhhdCB3ZSBhcmUgY2FsY3VhbHRpbmcgdGhpcyB2YXJpYWJsZSB1c2luZyB0aGUgVVNEIHByaWNlLCBhbmQgbm90IHRoZSBwcmljZSBpbiBCaXRjb2luLiAKCmBgYHtyfQojIGFkZCBkYWlseSBwcmljZSBjaGFuZ2UKYWx0X2RhdGFbLCBwY3RfY2hhbmdlIDo9IERlbHQocHJpY2VfdXNkdCksIGJ5ID0gcGFpcl91c2R0XQpgYGAKCiMjIyBOb3JtYWxpemVkIFByaWNlIGluIFVTRApTaW5jZSB0aGUgcHJpY2VzIHZhcnkgYSBsb3QsIGJvdGggb3ZlcnRpbWUgZm9yIHRoZSBzYW1lIGNvaW4gYW5kIGJldHdlZW4gY29pbnMsIHdlIHdpbGwgYWRkIGEgdmFyaWFibGUgb2YgdGhlIG5vcm1hbGl6ZWQgcHJpY2UgaW4gVVNELgoKYGBge3J9CiMgYWRkIG5vcm1hbGl6ZWQgcHJpY2VzIGluIHVkc3QKYWx0X2RhdGFbLCBwcmljZV91c2R0X25vcm0gOj0gcHJpY2VfdXNkdC9tYXgocHJpY2VfdXNkdCksIGJ5ID0gcGFpcl91c2R0XQpgYGAKCkxldCdzIGxvb2sgYXQgdGhlIHBlcmNlbnRhZ2UgZGFpbHkgY2hhbmdlcyBvZiB0aGUgYWx0Y29pbnMgYmV0d2VlbiAyMDE1IGFuZCB0b2RheS4KCmBgYHtyIHBlcmNlbnRhZ2VfZGFpbHlfY2hhbmdlLCBmaWcud2lkdGg9MTAsIGZpZy5oZWlnaHQ9NiwgZmlnLmNhcD0iUGVyY2VudGFnZSBkYWlseSByZXR1cm5zIGZvciBzb21lIGNvaW5zIn0KIyBwbG90IHRoZSBwZXJjZW50IGNoYW5nZXMKcCA8LSBnZ3Bsb3QoYWx0X2RhdGFbRGF0ZSA+IHltZCgiMjAxNS0wMS0wMSIpXSwgYWVzKHggPSBEYXRlLCB5ID0gICgxMDAqcGN0X2NoYW5nZSksIGNvbCA9IHBhaXJfdXNkdCkpICsgZ2VvbV9saW5lKCkKcCA8LSBwICsgZ2d0aXRsZSgiJSBEYWlseSBSZXR1cm5zIG92ZXIgdGltZSIpICsgeWxhYigiRGFpbHkgUmV0dXJuICglKSIpIApwIDwtIHAgKyB0aGVtZV9idygpICsgZ3VpZGVzKGNvbD1ndWlkZV9sZWdlbmQodGl0bGU9IkNvaW4gUGFpciIpKQpnZ3Bsb3RseShwKQpgYGAKCkFsdGhvdWdoIHRoZSBhYm92ZSBmaWd1cmUgaXMgdmVyeSBjbHV0dGVyZWQsIG9uZSB0aGluZyBpcyBjZXJ0YWluLCBwZXJjZW50YWdlIGRhaWx5IHJldHVybnMgdmFyeSBncmVhdGx5IGZvciBjcnlwdG8uIExldCdzIHRyeSB0byBtYWtlIHRoaXMgZmlndXJlIGEgYml0IGVhc2llciB0byByZWFkCgpgYGB7ciBwZXJjZW50YWdlX2RhaWx5X2NoYW5nZV8yLCBtZXNzYWdlPUZBTFNFLCBmaWcud2lkdGg9OCwgZmlnLmhlaWdodD02LCBmaWcuY2FwPSJQZXJjZW50YWdlIGRhaWx5IHJldHVybnMgZm9yIHNvbWUgY29pbnMifQpwIDwtIGdncGxvdChhbHRfZGF0YVtEYXRlID4geW1kKCIyMDE1LTAxLTAxIildLCBhZXMoeCA9IERhdGUsIHkgPSAgKDEwMCpwY3RfY2hhbmdlKSwgY29sID0gcGFpcl91c2R0KSkgKyBnZW9tX2xpbmUoKSArIGZhY2V0X3dyYXAofiBwYWlyX3VzZHQpCnAgPC0gcCArIGdndGl0bGUoIlBlcmNlbnRhZ2UgRGFpbHkgUmV0dXJucyBvdmVyIHRpbWUiKSArIHlsYWIoIkRhaWx5IFJldHVybiAoJSkiKSAKcCA8LSBwICsgdGhlbWVfYncoKSArIHRoZW1lKGxlZ2VuZC5wb3NpdGlvbj0ibm9uZSIpIApnZ3Bsb3RseShwKQpgYGAKCkl0IGlzIGtpbmQgb2Ygc3VycHJpc2luZyB0aGF0IEJpdGNvaW4gaGFzIHRoZSBsZWFzdCB2YXJpYWJpbGl0eSBpbiBkYWlseSByZXR1cm5zLiBUaGUgbmljZSBiaWcgc3Bpa2UgYXJvdW5kIEFwcmlsIDJuZCAyMDE3IHNob3dzIGEgcGVyY2VudGFnZSBkYWlseSByZXR1cm4gb2Ygfjg4JSBmb3IgWFJQLCB0aGlzIGlzIHRoZSBoaWdoZXN0IGRhaWx5IHJldHVybiBJIGhhdmUgc2VlbiEgCgpMZXQncyBsb29rIGF0IHRoZSBwZXJjZW50YWdlIGRhaWx5IHJldHVybnMgZm9yIEJpdGNvaW4gYW5kIExpdGVjb2luIHNpbmNlIHRoZXkgc2VlbSB0byBiZSBoaWdobHkgY29ycmVsYXRlZC4gSSBhbSBnb2luZyB0byB6b29tIGluIG9uIHRoZSB0aW1lIHBlcmlvZCAyMDE2LTAyLTAxIGFuZCAyMDE2LTA1LTAxLgoKYGBge3IgZGFpbHlfcmV0dXJuX2x0Y19idGMsIGZpZy53aWR0aD0xMCwgZmlnLmhlaWdodD02LCBmaWcuY2FwPSJEYWlseSBSZXR1cm4gZm9yIEJpdGNvaW4gYW5kIExUQyBpbiAyMDE4In0Kc3RhcnRfZGF0ZSA8LSB5bWQoIjIwMTYtMDItMDEiKQplbmRfZGF0ZSA8LSB5bWQoIjIwMTYtMDUtMDEiKQpwIDwtIGdncGxvdChhbHRfZGF0YVtwYWlyX3VzZHQgJWxpa2UlICJCVEN8TFRDIiAmIERhdGUgPiBzdGFydF9kYXRlICYgRGF0ZSA8IGVuZF9kYXRlXSwgYWVzKHggPSBEYXRlLCB5ID0gICgxMDAqcGN0X2NoYW5nZSksIGNvbCA9IHBhaXJfdXNkdCkpICsgZ2VvbV9saW5lKCkgKyB0aGVtZV9idygpICsgeWxhYigiUHJpY2UgKFVTRCkiKQpwCmBgYAoKVGhlcmUgY2xlYXJseSBpcyBhIGNvcnJlbGF0aW9uIGJldHdlZW4gZGFpbHkgcmV0dXJucyBvZiBCVEMgYW5kIExUQy4KCk5vdyB3ZSdsbCBzdWJzZXQgdGhlIGRhdGEgdG8gb25seSBrZWVwIHZhcmlhYmxlcyB3ZSBhcmUgaW50ZXJlc3RlZCBpbiAKCmBgYHtyfQojIHN1YnNldCBkYXRhCmFsdF9kYXRhX3N1YiA8LSBhbHRfZGF0YVssIC4oRGF0ZSwgcGFpcl91c2R0LCBwY3RfY2hhbmdlKV0KYGBgCgpXZSdsbCBkbyBzb21lIGRhdGEgcHJvY2Vzc2luZwoKYGBge3J9CiMgY29udmVydCB0byB3aWRlIGZvcm1hdAphbHRfZGF0YV9zdWIgPC0gc3ByZWFkKGRhdGEgPSBhbHRfZGF0YV9zdWIsIGtleSA9ICJwYWlyX3VzZHQiLCB2YWx1ZSA9ICJwY3RfY2hhbmdlIikKYGBgCgoKCg==